home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
United Public Domain Gold 2
/
United Public Domain Gold 2.iso
/
music_utilities
/
pt144.dms
/
pt144.adf
/
mod2midi
/
Mod2Midi_Source.lha
/
mod2midi.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-06-12
|
43KB
|
1,989 lines
/* .MOD to MID file converter ©1993 Andrew Scott
Amiga conversion © 1994 by Paul Huxham
*/
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <exec/types.h>
#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include "mod2midi.h"
#include "text.h"
/* CLI parameters */
#define OPT_TEMPLATE "MOD,MIDI,ABOUT/S,MAPSAMPLES/S,SAVEINFO/S,TRANSPOSE/S,DRUM/N,TEMPO/N,VOLUMESHIFT/S"
#define OPT_MODFILE 0
#define OPT_MIDIFILE 1
#define OPT_ABOUT 2
#define OPT_MAPSAMPLES 3
#define OPT_SAVEINFO 4
#define OPT_TRANSPOSE 5
#define OPT_DRUMCHANNEL 6
#define OPT_TEMPOTYPE 7
#define OPT_VOLUMESHIFT 8
#define OPT_NUMBEROF 9
/* Function Prototypes */
int Instrument( void );
void OutByte( bfile f, char b );
void FlushOut( bfile f );
void CloseOut( bfile f );
int OpenOut( bfile f, string fn );
unsigned long Beatle( bfile f );
unsigned char InByte( bfile f );
void CloseIn( bfile f );
int OpenIn( bfile f, string fn );
void Inskipp( bfile f, unsigned long n );
struct bpos FPos( bfile f );
void FGoto( bfile f, struct bpos x );
int WriteVLQ( bfile f, unsigned long i );
int MKTest( bfile f );
string SimplifyName( string s );
string GetLine( FILE *f );
int ReadModSpecs( bfile f, string n, samps s );
void ScanSamples( samps s );
int SetDefaults( samps s, string fn );
void SaveDefaults( samps s, string fn );
void NullArryFree( string *sp );
int Instrument();
string MIDIVal( samp *sam );
string TransVal( samp *sam );
string VolumeVal( samp *sam );
string NullVal( samp *sam );
int Sample( samps s, string (*MIDIVal)(samp *x), int w );
int ChooseChannels( samps s );
void MapSamples( samps s );
void SaveSamp( samp sam );
void SaveSamples( samps s );
void Transpositions( samps s );
void VolumeShifts( samps s );
unsigned char NoteValue( unsigned int n );
unsigned long NoteLength( unsigned char n, unsigned int l, unsigned int b );
void WriteHeader( bfile mf, unsigned char n );
unsigned int Trk0Info( bfile mf, string s );
void AddToLog( unsigned long a, unsigned long b );
void WriteLog( FILE *f );
unsigned char RestrictVol( int v );
void ConvertMOD( bfile f1, bfile f2, string tn, samps smp );
extern int ScrollChoice( string title, string *sp, int w );
extern string DialogBox( string *sp, string def );
char *version = "$VER: Mod2Midi 1.0 (12.6.94)";
long __stack = 20000; // Code allocates everything off the stack ;-)
bfile MidFile, ModFile;
char SongName[21];
samps Samples;
unsigned long PosLog[64];
int DrumChann = 9, TempoType = 1, PosI = 0;
string MidFN, ModFN;
char *TempoTypes[] = { "NORMAL", "EXTENDED" };
extern struct Library *DOSBase;
int main( int argc, char *argv[] )
{
int r;
UBYTE Mode = MODE_CONVERT;
MidFile->f = ModFile->f = NULL;
MidFN = ModFN = NULL;
/* Only work if launched from shell */
if ( argc != 0 )
{
struct RDArgs *RDArgs;
LONG opts[OPT_NUMBEROF];
opts[OPT_MODFILE] = 0;
opts[OPT_MIDIFILE] = 0;
opts[OPT_ABOUT] = 0;
opts[OPT_MAPSAMPLES] = 0;
opts[OPT_SAVEINFO] = 0;
opts[OPT_TRANSPOSE] = 0;
opts[OPT_DRUMCHANNEL] = 0;
opts[OPT_TEMPOTYPE] = 0;
opts[OPT_VOLUMESHIFT] = 0;
RDArgs = ReadArgs( OPT_TEMPLATE, opts, NULL );
if ( RDArgs != NULL )
{
/* Grab all set cli parameters */
if ( opts[OPT_MODFILE] != 0 )
{
ModFN = malloc( strlen( (char *)opts[OPT_MODFILE] ) + 1 );
if ( ModFN != NULL ) strcpy( ModFN, (char *)opts[OPT_MODFILE] );
}
if ( opts[OPT_MIDIFILE] != 0 )
{
MidFN = malloc( strlen( (char *)opts[OPT_MIDIFILE] ) + 1 );
if ( MidFN != NULL ) strcpy( MidFN, (char *)opts[OPT_MIDIFILE] );
}
if ( opts[OPT_ABOUT] != 0 ) Mode |= MODE_ABOUT;
if ( opts[OPT_MAPSAMPLES] != 0 ) Mode |= MODE_MAPSAMPLES;
if ( opts[OPT_SAVEINFO] != 0 ) Mode |= MODE_SAVEINFO;
if ( opts[OPT_TRANSPOSE] != 0 ) Mode |= MODE_TRANSPOSE;
if ( opts[OPT_VOLUMESHIFT] != 0 ) Mode |= MODE_VOLUMESHIFT;
if ( opts[OPT_DRUMCHANNEL] != 0 )
{
/* Drum channel is stored as value - 1 */
DrumChann = *( (ULONG *)opts[OPT_DRUMCHANNEL] ) - 1;
printf( "Drum channel set to: %d\n", DrumChann + 1 );
}
if ( opts[OPT_TEMPOTYPE] != 0 )
{
int value;
value = *( (ULONG *)opts[OPT_TEMPOTYPE] );
if ( value >= 0 && value <= 1 )
{
TempoType = value;
printf( "Tempo type set to: %s\n", TempoTypes[TempoType] );
}
else printf( "Illegal value for TEMPO, using default of NORMAL\n" );
}
FreeArgs ( RDArgs );
/* Open the MID file */
if ( MidFN != NULL )
{
r = OpenOut( MidFile, MidFN );
}
/* Open the MOD file */
if ( r != NULL && ModFN != NULL )
{
if ( ModFile->f != NULL ) SaveDefaults( Samples, ModFN ); // Save defaults
r = OpenIn( ModFile, ModFN );
if ( r != NULL )
{
if ( !ReadModSpecs( ModFile, SongName, Samples ) )
printf( "%s is not a .mod file\n", ModFN );
else
if ( !SetDefaults( Samples, ModFN ) ) CloseIn( ModFile );
}
}
if ( ( Mode & MODE_ABOUT ) == MODE_ABOUT )
{
int i = 0;
while ( MSG_ABOUT[i] != NULL ) printf( "%s\n", MSG_ABOUT[ i++ ] );
Mode = NULL; //Dont allow futher processing
}
if ( ( Mode & MODE_MAPSAMPLES ) == MODE_MAPSAMPLES )
{
if ( ModFile->f != NULL ) MapSamples( Samples );
else
printf( "%s", MSG_ERR_NO_MOD_FILENAME );
}
if ( ( Mode & MODE_SAVEINFO ) == MODE_SAVEINFO )
{
if ( ModFile->f != NULL ) SaveSamples( Samples );
else
printf( "%s", MSG_ERR_NO_MOD_FILENAME );
}
if ( ( Mode & MODE_TRANSPOSE ) == MODE_TRANSPOSE )
{
if ( ModFile->f != NULL ) Transpositions( Samples );
else
printf( "%s", MSG_ERR_NO_MOD_FILENAME );
}
if ( ( Mode & MODE_VOLUMESHIFT ) == MODE_VOLUMESHIFT )
{
if ( ModFile->f != NULL ) VolumeShifts( Samples );
else
printf( "%s", MSG_ERR_NO_MOD_FILENAME );
}
/* Do the conversion */
if ( MidFile->f != NULL && ModFile->f != NULL && Mode != NULL )
{
if ( ChooseChannels( Samples ) <= 16 )
{
ConvertMOD( ModFile, MidFile, SongName, Samples );
}
else
printf( "Too many instruments, there is a maximum of 16 allowed\n" );
}
else
printf( "Both a source .MOD and a destination .MID file must be supplied\n" );
/* If the file is open, close it */
if ( ModFile->f != NULL )
{
CloseIn( ModFile );
SaveDefaults( Samples, ModFN );
}
/* If a filename has been allocated, free it */
if ( ModFN != NULL ) free( ModFN );
/* If the file is open, close it */
if ( MidFile->f != NULL ) CloseOut( MidFile );
/* If a filename has been allocated, free it */
if ( MidFN != NULL ) free( MidFN );
}
else printf( "Illegal parameters!\n" );
}
}
/* Post: The byte b has been written to the buffer of the file f */
void OutByte( bfile f, char b )
{
if ( f->o == BUFFSIZE )
{
fwrite( f->b, 1, BUFFSIZE, f->f );
f->o = 0;
}
f->b[ f->o++ ] = b;
}
/* Pre: f was opened for writing */
/* Post: The file f has has its buffer flushed */
void FlushOut( bfile f )
{
if ( f->o > 0 ) fwrite( f->b, 1, f->o, f->f );
f->o = 0;
}
/* Pre: f was opened for writing */
/* Post: The file f has been flushed and is now closed */
void CloseOut( bfile f )
{
FlushOut( f );
fclose( f->f );
f->f = NULL;
}
/* Returns: NZ if the file f has been opened for writing with the name fn */
int OpenOut( bfile f, string fn )
{
if ( f->f != NULL ) CloseOut( f );
f->f = fopen( fn, "wb" );
f->o = 0;
return f->f != NULL;
}
/* Returns: bfile-tell; the offset from the start */
unsigned long Beatle( bfile f )
{
return ftell( f->f ) + f->o;
}
/* Pre: f was opened for reading */
/* Returns: The next byte from the file f */
unsigned char InByte( bfile f )
{
if ( f->o == BUFFSIZE && !feof( f->f ) )
{
f->r = fread( f->b, 1, BUFFSIZE, f->f );
if ( f->r < BUFFSIZE ) f->b[ f->r ] = 0;
f->o = 0;
}
if ( f->o < f->r ) return f->b[ f->o++ ];
return f->b[ f->o ];
}
/* Post: The file f is now closed */
void CloseIn( bfile f )
{
fclose( f->f );
f->f = NULL;
}
/* Returns: NZ if the file f has been opened for reading with the name fn */
int OpenIn( bfile f, string fn )
{
if ( f->f != NULL ) CloseIn( f );
f->f = fopen( fn, "rb" );
f->o = f->r = BUFFSIZE;
return f->f != NULL;
}
/* Pre: f was opened for reading */
/* Post: f's file pointer has skipped forward n bytes */
void Inskipp( bfile f, unsigned long n )
{
n += f->o;
while ( n >= BUFFSIZE && !feof( f->f ) )
{
f->r = fread( f->b, 1, BUFFSIZE, f->f );
if ( f->r < BUFFSIZE ) f->b[ f->r ] = 0;
n -= BUFFSIZE;
}
f->o = n; // May cause an error if was eof
}
/* Returns: All necessary information regarding file f's status */
struct bpos FPos( bfile f )
{
struct bpos x;
x.d = *f;
x.p = ftell( f->f );
return x;
}
/* Pre: x was the status of f previously */
/* Post: File f has had its status changed to x */
void FGoto( bfile f, struct bpos x )
{
fseek( f->f, x.p, SEEK_SET );
*f = x.d;
}
/*
* Returns: # of bytes written after a variable-length-quantity equivalent
* of i has been written to the file f.
*/
int WriteVLQ( bfile f, unsigned long i )
{
int x = 0;
unsigned long buffer;
buffer = i & 127;
while ( (i >>= 7 ) > 0 )
buffer = ( ( buffer << 8 ) | 128 ) + ( i & 127 );
while ( 1 )
{
OutByte( f, buffer & 255 );
x++;
if ( buffer & 128 ) buffer >>= 8;
else return x;
}
}
/* Returns: The number of samples in the Module file f */
int MKTest( bfile f )
{
unsigned long offset;
int i = 15;
char s[4];
offset = ftell( f->f );
if ( !fseek( f->f, 1080, SEEK_SET ) )
{
fread( s, 1, 4, f->f );
if ( !memcmp( s, "M.K.", 4 ) || !memcmp( s, "M!K!", 4 ) ||
!memcmp( s, "FLT4", 4 ) ) i = 31;
else if ( !memcmp( s, "FLT8", 4 ) ) i = 0;
}
fseek( f->f, offset, SEEK_SET );
return i;
}
/*
* Returns: A string similar to s, but has had any nasty headers removed
* any leading spaces or trailing spaces, and all made lower-case with
* any intermediate spaces replaced by underbar-characters
*/
string SimplifyName( string s )
{
string t, r, x;
x = strchr( s, ':' );
if ( x != NULL && x - s == 5 && tolower( s[0] ) == 's' &&
tolower( s[1] ) == 't' && s[2] == '-' )
{
r = x = (string)malloc( 18 );
t = s + 6;
while( *(x++) = *(t++) ); // Remove soundtracker header
} else strcpy( r = (string)malloc( 1 + strlen(s) ), s );
for ( t = r; *t == ' '; t++ );
x = r;
while ( *(x++) = *(t++) ); // Remove leading spaces
if ( *r )
{
x--;
while ( *(--x) == ' ' ) // Remove trailing spaces
*x = 0;
}
/* Fill intermediate spaces with '_' */
for ( x = r; *x; x++ ) if ( *x == ' ' ) *x = '_';
t = r = (string)realloc( r, strlen(r) + 1 );
for ( ; *t; t++ ) *t = tolower( *t ); // Make the lot lower-case
return r;
}
/* Returns: Next line from file f, NULL on error/eof */
string GetLine( FILE *f )
{
string s, t;
int c;
if ( ( t = s = (string)malloc( MAXSTRING ) ) == NULL )
{
printf( "%s", MSG_ERR_OUT_OF_MEMORY );
return NULL;
}
while ( ( c = fgetc( f ) ) != EOF && c != '\n' ) *(t++) = c;
if ( s == t )
{
free( s );
return NULL;
}
*t = 0;
return (string)realloc( s, t - s + 1 );
}
/*
* Returns: Z if f is not a supported Amiga Module, else n is set to
* be the name of the Module and s is set to hold sample information
*/
int ReadModSpecs( bfile f, string n, samps s )
{
unsigned char b, c;
int i, okmodule;
string t1;
samp *t2;
for ( i = 20, t1 = n; i--; *(t1++) = InByte( f ) );
okmodule = !*n || isprint( *n );
*t1 = 0;
c = s->n = MKTest( f );
okmodule = okmodule && c;
for ( t2 = s->s; c--; t2++ )
{
for ( i = 22, t1 = t2->n; i--; *(t1++) = InByte( f ) );
okmodule = okmodule && ( !*( t2->n ) || isprint( *(t2->n) ) );
*t1 = 0;
b = InByte( f );
t2->l = 256 * b + InByte( f );
/* If the sample is this small, not worth processing */
if ( t2->l < 4 ) t2->l = 0;
b = InByte( f );
t2->v = InByte( f );
InByte( f );
InByte( f );
b = InByte( f );
/* Looping: plays 'forever' */
if ( 256 * b + InByte( f ) > 1 && t2->l ) t2->l = -1;
t2->m = 0;
}
return !feof( f->f ) && okmodule;
}
/*
* Post: Sample data for ModFile has been scanned and samples in s have
* been given "reasonable" values for volume shifting and transpositions
* I hope :)
*/
void ScanSamples( samps s )
{
samp *sam;
unsigned int maxpat = 0, i, j, k;
unsigned char x;
signed char c[SCANSIZE];
signed char max, min, *p;
unsigned long offset, len, lenarry[31];
long sum;
double ratio, junk;
offset = ftell( ModFile->f );
fseek( ModFile->f, 42, SEEK_SET );
for ( i = 0; i < s->n; i++ )
{
fread( &x, 1, 1, ModFile->f );
lenarry[i] = 256 * x;
fread( &x, 1, 1, ModFile->f );
lenarry[i] += x;
lenarry[i] *= 2;
fseek( ModFile->f, 28, SEEK_CUR );
}
fseek( ModFile->f, 20 + 30 * s->n, SEEK_SET );
fread( &x, 1, 1, ModFile->f );
fread( c, 1, 129, ModFile->f );
for ( ; x; x-- ) if ( c[x] > maxpat ) maxpat = c[x];
len = 1024L * ( maxpat + 1 ) + 30 * s->n + ( ( s->n > 30 ) ? 4 : 0 ) + 150;
fseek( ModFile->f, len, SEEK_SET );
for ( i = 0, sam = s->s; i < s->n; i++, sam++ )
{
/* Set transposition values to 0 */
sam->t[0] = 0;
sam->t[1] = 0;
sam->t[2] = 0;
sam->a[0] = 0;
/* No sample = no volume shifting */
if ( !sam->l )
{
sam->a[1] = 1;
sam->a[2] = 1;
fseek( ModFile->f, lenarry[i], SEEK_CUR );
}
else
{
len = min( lenarry[i], SCANSIZE );
if ( !( j = len = fread( c, 1, len, ModFile->f ) ) ) len = 1;
fseek( ModFile->f, lenarry[i] - j, SEEK_CUR );
min = 127;
max = -128;
sum = 0;
for ( p = c; j--; p++ )
{
sum += *p;
if ( *p > max ) max = *p;
if ( *p < min ) min = *p;
}
/* Get average .. normally ~0 */
ratio = 1.0 * sum / len;
if ( fabs( max - ratio ) > fabs( min - ratio ) )
ratio = fabs( min - ratio ) / 64.0;
else
ratio = fabs( max - ratio ) / 64.0;
j = k = 1;
/* Now find decent rational approx. */
while ( k++ < 15 )
if ( fabs( modf( ratio * k, &junk ) - .5 ) > fabs( modf( ratio * j, &junk ) - .5 ) )
j = k;
if ( !( sam->a[1] = ratio * j + 0.5 ) )
{
/* If really small scaling needed.. */
sam->a[1] = 1;
sam->a[2] = 16;
}
else sam->a[2] = j;
}
}
fseek( ModFile->f, offset, SEEK_SET );
}
/*
* Returns: NZ if the samples in s have been sucessfully allocated default
* values corresponding to definitions in the DEF_MAPFILE file, and from
* a .mm file corresponding to the filename fn
*/
int SetDefaults( samps s, string fn )
{
FILE *f;
char i, m[MAXSTRING];
int d, e[6], v;
samp *sam;
string n, t;
bfile mmf;
t = strchr( strcpy( m, fn ), '.' );
if ( t == NULL )
{
i = strlen( m );
m[i] = '.';
}
else i = t - m;
m[++i] = 'm';
m[++i] = 'm';
m[++i] = 0;
mmf->f = NULL;
if ( OpenIn( mmf, m ) )
{
for ( i = s->n, sam = s->s; i--; sam++ )
{
sam->m = InByte( mmf );
sam->t[0] = (signed char)InByte( mmf );
sam->t[1] = (signed char)InByte( mmf );
sam->t[2] = (signed char)InByte( mmf );
sam->a[0] = (signed char)InByte( mmf );
sam->a[1] = (signed char)InByte( mmf );
sam->a[2] = (signed char)InByte( mmf );
}
CloseIn( mmf );
}
else ScanSamples( s );
if ( ( f = fopen( DEF_MAPFILE, "rt" ) ) == NULL )
{
printf( "%s: Not found\n", DEF_MAPFILE );
return 0;
}
i = s->n;
for ( sam = s->s; i--; sam++ )
if ( sam->l )
{
n = SimplifyName( sam->n );
t = GetLine( f );
e[3] = 0; // These are the default volume constants
e[4] = 1;
e[5] = 1;
sscanf( t, "%s %d %d %d %d %d %d %d", m, &d, &e[0], &e[1], &e[2], &e[3], &e[4], &e[5] );
if ( ( v = strcmp( m, n ) ) > 0 )
{
rewind( f );
free( t );
t = GetLine( f );
e[3] = 0;
e[4] = 1;
e[5] = 1;
sscanf( t, "%s %d %d %d %d %d %d %d", m, &d, &e[0], &e[1], &e[2], &e[3], &e[4], &e[5] );
v = strcmp( m, n );
}
free( t );
while ( v < 0 && ( t = GetLine( f ) ) != NULL )
{
e[3] = 0;
e[4] = 1;
e[5] = 1;
sscanf( t, "%s %d %d %d %d %d %d %d", m, &d, &e[0], &e[1], &e[2], &e[3], &e[4], &e[5] );
free( t );
v = strcmp( m, n );
}
if ( !v )
{
sam->m = d;
sam->t[0] = e[0];
sam->t[1] = e[1];
sam->t[2] = e[2];
sam->a[0] = e[3];
sam->a[1] = e[4];
sam->a[2] = e[5];
}
free( n );
}
fclose( f );
return 1;
}
/*
* Post: The samples attributes in s have been written to a .mm file
* corresponding to the filename fn
*/
void SaveDefaults( samps s, string fn )
{
char m[MAXSTRING];
string t;
char i;
bfile mmf;
samp *sam;
t = strchr( strcpy( m, fn ), '.' );
if ( t == NULL )
{
i = strlen( m );
m[i] = '.';
}
else i = t - m;
m[++i] = 'm';
m[++i] = 'm';
m[++i] = 0;
mmf->f = NULL;
if ( !OpenOut( mmf, m ) ) return;
for ( i = s->n, sam = s->s; i--; sam++ )
{
OutByte( mmf, sam->m );
OutByte( mmf, sam->t[0] );
OutByte( mmf, sam->t[1] );
OutByte( mmf, sam->t[2] );
OutByte( mmf, sam->a[0] );
OutByte( mmf, sam->a[1] );
OutByte( mmf, sam->a[2] );
}
CloseOut( mmf );
}
/* Post: The NULL-terminated array sp is gone */
void NullArryFree( string *sp )
{
string *t;
t = sp;
while ( *t != NULL ) free( *(t++) );
free( sp );
}
/* Returns: MIDI instrument selected from file DEF_INSFILE */
int Instrument( void )
{
static int w = 0, doff = -1;
static string *sp = NULL;
int c;
if ( sp == NULL )
{
FILE *f;
string *t, s;
int x, i = 1;
if ( ( f = fopen( DEF_INSFILE, "rt" ) ) == NULL )
{
printf( "%s: Not found\n", DEF_INSFILE );
return -1;
}
if ( ( t = sp = (string *)malloc( 257 * sizeof( string ) ) ) == NULL )
{
printf( "%s", MSG_ERR_OUT_OF_MEMORY );
return -1;
}
while ( ( s = GetLine( f ) ) != NULL )
if ( x = strlen( s ) ) // Ignore blank lines
{
i++;
if ( x > w ) w = x;
*(t++) = s;
/* Take note of first drum position */
if ( doff < 0 && *s == 'D' ) sscanf( s, "D%d ", &doff );
}
*t = NULL;
sp = (string *)realloc( sp, i * sizeof( string ) ); // i <= 257
fclose( f );
}
c = 0;
c = ScrollChoice( "MIDI Instruments", sp, w );
if ( c > 127 ) c += doff;
return c;
}
/* Returns: a string representing the MIDI instrument *sam */
string MIDIVal( samp *sam )
{
string s;
s = (string)malloc( 5 );
if ( sam->m < 128 ) sprintf( s, "%4d", sam->m );
else sprintf( s, "D%3d", sam->m - 128 );
return s;
}
/* Returns: a string representing the transpose amount of *sam */
string TransVal( samp *sam )
{
string s;
s = (string)malloc( 15 );
if ( !sam->t[1] ) sprintf( s, " %4d", sam->t[0] );
else
if ( !sam->t[2] ) sprintf( s, " %4d,%4d", sam->t[0], sam->t[1] );
else sprintf( s, "%4d,%4d,%4d", sam->t[0], sam->t[1], sam->t[2] );
return s;
}
/* Returns: a string representing the volume shifting formula for *sam */
string VolumeVal( samp *sam )
{
string s;
s = (string)malloc( 17 );
if ( sam->a[0] < 0 )
sprintf( s, "(? -%3d)*%3d/%3d", abs(sam->a[0]), sam->a[1], sam->a[2] );
else
sprintf( s, "(? +%3d)*%3d/%3d", sam->a[0], sam->a[1], sam->a[2] );
return s;
}
/* Returns: an empty string */
string NullVal( samp *sam )
{
string s;
*( s = (string)malloc( 1 ) ) = 0;
return s;
}
/* Returns: Sample selected from list of samples, -1 on error */
int Sample( samps s, string (*MIDIVal)(samp *x), int w )
{
string *sp, *t, p;
samp *sam;
int i;
if ( ( t = sp = (string *)malloc( ( s->n + 1 ) * sizeof( string ) ) ) == NULL )
{
printf( "%s", MSG_ERR_OUT_OF_MEMORY );
return -1;
}
for ( i = s->n, sam = s->s; i--; sam++ )
{
*t = (string)malloc( 25 + w );
p = MIDIVal( sam );
sprintf( *t, "%c%-22s %s", (sam->l) ? '*' : ' ', sam->n, p );
free( p );
t++;
}
*t = NULL;
i = ScrollChoice( "Select Sample", sp, 24 + w );
NullArryFree( sp );
return i;
}
/*
* Returns: The number of different channels needed to play instruments. If
* that number is not greater than 16, upto 16 channels will be allocated
* to the samples.
*/
int ChooseChannels( samps s )
{
unsigned char c, d = 0, i[128], m, n, numchan;
samp *sam1, *sam2;
n = s->n;
sam1 = s->s;
memset( i, 0, 128 );
for ( n = s->n, sam1 = s->s; n--; sam1++ )
{
sam1->c = -1;
if ( sam1->l )
if ( sam1->m > 127 )
{
d = 1;
sam1->c = DrumChann;
}
else i[ sam1->m ] = 1;
else sam1->m = 0;
}
for ( numchan = d, n = 128; n--; numchan += i[n] );
if ( numchan > 16 ) return numchan;
/* Ok.. now we must go through and set channels appropriately */
m = s->n;
sam1 = s->s;
c = 0;
while ( m-- )
{
if ( sam1->c < 0 )
{
sam1->c = c;
n = m;
sam2 = sam1 + 1;
while ( n-- )
{
if ( sam2->c < 0 )
if ( sam2->m == sam1->m || ! sam2->l ) sam2->c = c;
sam2++;
}
if ( ++c == DrumChann && d )
c++;
}
sam1++;
}
return numchan;
}
/* Post: The samples s have been allocated appropriate instruments, we hope */
void MapSamples( samps s )
{
int i, j;
do
if ( ( i = Sample( s, MIDIVal, 4 ) ) >= 0 && ( j = Instrument() ) >= 0 )
s->s[i].m = j;
while ( i>=0 );
}
/* Post: The sample sam has been updated in the DEF_MAPFILE file */
void SaveSamp( samp sam )
{
FILE *f1, *f2;
string s, n;
int v, d;
char m[MAXSTRING];
if ( !sam.l ) return;
if ( ( f1 = fopen( DEF_MAPFILE, "rt" ) ) == NULL )
{
printf( "%s: Not found\n", DEF_MAPFILE );
return;
}
f2 = fopen( "temp.$$$", "wt" );
n = SimplifyName( sam.n );
v = 1;
while ( v > 0 && ( s = GetLine( f1 ) ) != NULL )
{
sscanf( s, "%s %d", m, &d );
if ( ( v = strcmp( n, m ) ) <= 0 )
{
fprintf( f2, "%s %d %d %d %d %d %d %d\n", n, sam.m, sam.t[0], sam.t[1], sam.t[2], sam.a[0], sam.a[1], sam.a[2] );
free( n );
n = NULL;
if ( v ) fprintf( f2, "%s\n", s );
}
else fprintf( f2, "%s\n", s );
free( s );
}
while ( ( s = GetLine( f1 ) ) != NULL )
{
fprintf( f2, "%s\n", s );
free( s );
}
if ( n != NULL )
{
fprintf( f2, "%s %d %d %d %d %d %d %d\n", n, sam.m, sam.t[0], sam.t[1], sam.t[2], sam.a[0], sam.a[1], sam.a[2] );
free( n );
}
fclose( f2 );
fclose( f1 );
if ( unlink( DEF_MAPFILE ) < 0 || rename( "temp.$$$", DEF_MAPFILE ) < 0 )
printf( "File %s cannot be updated. Instrument map changes cannot be made\n" );
}
/* Post: The desired sample attributes of s have been saved to disk */
void SaveSamples( samps s )
{
int i;
do
if ( ( i = Sample( s, NullVal, 0 ) ) >= 0 ) SaveSamp( s->s[i] );
while ( i >= 0 );
}
/* Post: All necessary transpositions have been applied to the samples s */
void Transpositions( samps s )
{
int i;
do
if ( ( i = Sample( s, TransVal, 14 ) ) >= 0 )
{
char s1[41];
string s2;
samp *sam;
sam = s->s + i;
if ( !sam->t[1] ) sprintf( s1, "%d", sam->t[0] );
else
if ( !sam->t[2] ) sprintf( s1, "%d,%d", sam->t[0], sam->t[1] );
else sprintf( s1, "%d,%d,%d", sam->t[0], sam->t[1], sam->t[2] );
s2 = (string)malloc( 10 );
strcpy( s2, "0,1,1" );
s2 = DialogBox( MSG_ENTER_TRANSPOSITION, s1 );
sam->t[1] = 0;
sam->t[2] = 0;
sscanf( s2, "%d,%d,%d", &sam->t[0], &sam->t[1], &sam->t[2] );
free( s2 );
if ( ( sam->t[0] || sam->t[1] ) && sam->m > 127 )
printf( "Warning: transposition value for percussion instrument is changed\n" );
if ( sam->t[0] <- 128 || sam->t[0] > 127 || sam->t[1] < -128 ||
sam->t[1] > 127 || sam->t[2] <- 128 || sam->t[2] > 127 )
{
printf( "Warning: Transposition value out of range, resetting to 0\n" );
sam->t[0] = 0;
sam->t[1] = 0;
sam->t[2] = 0;
}
}
while ( i >= 0 );
}
/* Post: All volume shifting formulae have been defined for the samples s */
void VolumeShifts( samps s )
{
int i;
do
{
i = Sample( s, VolumeVal, 16 );
if ( i >= 0 )
{
char s1[41];
string s2;
samp *sam;
sam = s->s + i;
sprintf( s1, "%d,%d,%d", sam->a[0], sam->a[1], sam->a[2] );
s2 = (string)malloc( 10 );
s2 = DialogBox( MSG_ENTER_VOLUMESHIFT, s1 );
if ( sscanf( s2, "%d,%d,%d", &sam->a[0], &sam->a[1], &sam->a[2]) != 3 ||
sam->a[0] > 127 || sam->a[0] < -128 || sam->a[1] > 127 ||
sam->a[1] < 0 || sam->a[2] > 127 || sam->a[2] < 1 )
{
printf( "Warning: volume-shift values out of range, resetting to 0, 1, 1\n" );
sam->a[0] = 0;
sam->a[1] = 1;
sam->a[2] = 1;
}
free( s2 );
}
}
while ( i >= 0 );
}
/* Returns: MIDI note equivalent of MOD period-lengths */
unsigned char NoteValue( unsigned int n )
{
static unsigned int t[72] =
{
1712, 1616, 1525, 1440, 1357, 1281, 1209, 1141, 1077, 1017, 961, 907,
856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57,
53, 50, 48, 45, 42, 40, 38, 36, 34, 32, 30, 28
};
signed char a = 0, m = 35, b = 71;
if ( !n ) return 0;
/* Binary search */
for ( ; b-a > 1; m = ( a + b ) / 2 )
if ( t[m] < n ) b = m;
else a = m;
/* Choose closest value */
if ( n - t[b] < t[a] - n ) return (unsigned char)( b + 36 );
return (unsigned char )( a + 36 );
}
/* Returns: # of ticks to play note length l at pitch n, b beats/min */
unsigned long NoteLength( unsigned char n, unsigned int l, unsigned int b )
{
static float t[84] =
{
3.200e-3, 3.020e-3, 2.851e-3, 2.691e-3, 2.540e-3, 2.397e-3,
2.263e-3, 2.136e-3, 2.016e-3, 1.903e-3, 1.796e-3, 1.695e-3,
1.600e-3, 1.510e-3, 1.425e-3, 1.345e-3, 1.270e-3, 1.197e-3,
1.131e-3, 1.068e-3, 1.008e-3, 9.514e-4, 8.980e-4, 8.476e-4,
8.000e-4, 7.551e-4, 7.127e-4, 6.727e-4, 6.350e-4, 5.993e-4,
5.657e-4, 5.339e-4, 5.040e-4, 4.757e-4, 4.490e-4, 4.238e-4,
4.000e-4, 3.775e-4, 3.564e-4, 3.364e-4, 3.175e-4, 2.997e-4,
2.828e-4, 2.670e-4, 2.520e-4, 2.378e-4, 2.245e-4, 2.119e-4,
2.000e-4, 1.888e-4, 1.782e-4, 1.682e-4, 1.587e-4, 1.498e-4,
1.414e-4, 1.335e-4, 1.260e-4, 1.189e-4, 1.122e-4, 1.059e-4,
1.000e-4, 9.439e-5, 8.909e-5, 8.409e-5, 7.937e-5, 7.492e-5,
7.071e-5, 6.674e-5, 6.300e-5, 5.946e-5, 5.612e-5, 5.297e-5,
5.000e-5, 4.719e-5, 4.454e-5, 4.204e-5, 3.969e-5, 3.746e-5,
3.536e-5, 3.337e-5, 3.150e-5, 2.973e-5, 2.806e-5, 2.649e-5
}; /* Multipliers for each pitch: 12th roots of 2 apart */
/* Better not slide out of this range */
return (unsigned long)( t[n - 36] * b * l );
}
/* Post: The MIDI header has been written to mf, with #tracks = n */
void WriteHeader( bfile mf, unsigned char n )
{
static unsigned char MIDIH[14] =
{
77, 84, 104, 100, 0, 0, 0, 6, 0, 1, 0, (unsigned char)-1, 0, 192
};
int i = 0;
MIDIH[11] = n + 1;
while ( i < 14 )
OutByte( mf, MIDIH[i++] );
}
/*
* Returns: the number of bytes written as track 0 so far, given mf as the
* output MIDI file. s is the name of the tune.
*/
unsigned int Trk0Info( bfile mf, string s )
{
static unsigned char TRK0I[63] =
{
0, 255, 2, 42, 70, 105, 108, 101, 32, 67, 111, 112, 121, 114, 105, 103,
104, 116, 32, 40, 99, 41, 32, 49 ,57, 57, 51, 32, 65, 100, 114, 101, 110,
97, 108, 105, 110, 32, 83, 111, 102, 116, 119, 97, 114, 101,
0, 255, 88, 4, 3, 2, 24, 8,
0, 255, 89, 2, 0, 0,
0, 255, 3
}; /* Standard header + copyright message */
unsigned int i = 0;
while ( i < 63 ) OutByte( mf, TRK0I[i++] );
i = 64 + strlen( s );
OutByte( mf, strlen( s ) );
while ( *s ) OutByte( mf, *(s++) );
return i;
}
/* Post: a (file position) and b (number) have been added to the log */
void AddToLog( unsigned long a, unsigned long b )
{
PosLog[ PosI++ ] = b;
PosLog[ PosI++ ] = a;
}
/* Post: PosLog has been written into file f */
void WriteLog( FILE *f )
{
unsigned long x, y;
unsigned char c[4];
if ( !PosI ) return;
x = ftell( f );
while ( PosI )
{
fseek( f, PosLog[ --PosI ], SEEK_SET );
y = PosLog[ --PosI ];
c[3] = y & 255;
y >>= 8;
c[2] = y & 255;
y >>= 8;
c[1] = y & 255;
y >>= 8;
c[0] = y;
fwrite( c, 1, 4, f );
}
fseek( f, x, SEEK_SET );
}
/* Returns: A volume in the range 0..127 */
unsigned char RestrictVol( int v )
{
if ( v < 0 ) return 0;
else return (unsigned char)( ( v > 127 ) ? 127 : v );
}
/*
* Post: The Amiga MODfile f1 has been converted and written to MIDI file f2,
* using instrument mappings smp. Tune has a name tn.
*/
void ConvertMOD( bfile f1, bfile f2, string tn, samps smp )
{
unsigned char b, length, pattern[128];
string n;
unsigned int i, j, k, tempdone = 0;
samp *sam;
struct bpos p1, p2;
p1 = FPos( f1 );
length = InByte( f1 );
InByte( f1 );
for ( i = 0; i < 128; pattern[i++] = InByte( f1 ) );
Inskipp( f1, ( smp->n > 15 ) ? 4 : 0 );
WriteHeader( f2, smp->n );
p2 = FPos( f1 );
for ( i = 0, sam = smp->s - 1; i <= smp->n; sam++, i++ )
{
unsigned long cnt, inst, timer, delay[4];
unsigned char c;
OutByte( f2, 77 );
OutByte( f2, 84 );
OutByte( f2, 114 );
OutByte( f2, 107 );
inst = Beatle( f2 ); /* Store this position so can set AFTERWARDS */
OutByte( f2, 0 );
OutByte( f2, 0 );
OutByte( f2, 0 );
OutByte( f2, 0 );
if ( !i ) cnt = Trk0Info( f2, tn );
else
{
OutByte( f2, 0 );
OutByte( f2, (unsigned char)255 );
OutByte( f2, 3 );
b = strlen( n = sam->n );
OutByte( f2, b );
cnt = 7 + b;
while ( b-- ) OutByte( f2, *(n++) );
c = sam->c;
OutByte( f2, 0 );
OutByte( f2, 0xC0 + c ); // Set channel
OutByte( f2, ( sam->m > 127 ) ? 126 : sam->m );
}
timer = 0;
if ( sam->l || !i )
{
unsigned int bpm, ticks, l, effect, h;
unsigned char sampnum, lastsam[4] = { 0 }, vol[4];
signed char patbreak;
unsigned long x, pause;
int note, lastslide, slideto;
int n[4][48][2]; // Note data for a song position
int lastn[4]; // Last note on a particular channel
printf( "Converting sample %d\n", i );
memset( lastn, 0, 4 * sizeof( int ) );
memset( vol, 0, 4 );
memset( delay, 0, 4 * sizeof( long ) );
bpm = 125;
ticks = 6;
lastslide = slideto = 0;
patbreak = 0;
for ( h = 48; h--; )
for ( k = 4; k--; )
n[k][h][0] = 0;
for ( l = 0; l < length; l++ )
{
if ( pattern[l] <= pattern[ l - 1 ] || !l )
{
FGoto( f1, p2 );
x = (unsigned)1024 * pattern[l];
}
else x = (unsigned)1024 * ( pattern[l] - pattern[ l - 1 ] - 1 );
Inskipp( f1, x );
j = 64;
if ( patbreak > 0 ) patbreak = 1 - patbreak;
while ( j-- )
{
pause = 0;
if ( patbreak ) Inskipp( f1, 16 );
else
for ( k = 0; k < 4; k++ )
{
n[k][0][1] = sam->v;
sampnum = InByte( f1 );
note = ( ( sampnum & 15 ) << 8 ) + InByte( f1 );
effect = InByte( f1 );
sampnum = ( sampnum & ~15 ) + ( effect >> 4 );
if ( !i ) note = 0;
if ( ( note || sampnum ) && delay[k] )
{
/* Stop old note */
cnt += 3 + WriteVLQ( f2, timer );
timer = 0;
OutByte( f2, 0x80 + c ); // Note off
OutByte( f2, ENOTE( lastn[k], 0 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[1] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x80 + c );
OutByte( f2, ENOTE( lastn[k], 1 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[2] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x80 + c );
OutByte( f2, ENOTE( lastn[k], 2 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
}
}
delay[k] = 0;
}
/* Check "defaults" */
if ( !note && sampnum == i ) note = lastn[k];
else
if ( !sampnum )
if ( lastsam[k] == i ) sampnum = i;
else note = 0;
else
{
if ( sampnum != i ) note = 0;
lastsam[k] = sampnum;
}
n[k][0][0] = note;
effect = ( ( effect & 15 ) << 8 ) + InByte( f1 );
switch ( effect & 0xF00 )
{
/* Arpeggio */
case 0x000:
{
int nv;
if ( !i || !effect || sam->m > 127 ) break;
if ( !note )
if ( !delay[k] ) break;
else
{
nv = NoteValue( lastn[k] );
n[k][47][0] = lastn[k];
n[k][47][1] = vol[k];
if ( effect & 0x0F0 ) n[k][16][0] = -( nv + ( ( effect & 0x0F0 ) >> 4 ) );
n[k][16][1] = vol[k];
if ( effect & 0x00F ) n[k][32][0] = -( nv + ( effect & 0x00F ) );
n[k][32][1] = vol[k];
}
else
{
nv = NoteValue( note );
n[k][47][0] = note;
n[k][47][1] = sam->v;
if ( effect & 0x0F0 ) n[k][16][0] = -( nv + ( ( effect & 0x0F0 ) >> 4 ) );
n[k][16][1] = sam->v;
if ( effect & 0x00F ) n[k][32][0] = -( nv + ( effect & 0x00F ) );
n[k][32][1] = sam->v;
}
break;
}
/* Slide to */
case 0x300:
if ( !note && !slideto || note == lastn[k] || sam->m > 127 ) break;
if ( effect & 0x0FF ) lastslide = effect & 0x0FF;
else lastslide = abs( lastslide );
if ( note ) slideto = note;
if ( slideto > lastn[k] )
{
n[k][0][0] = lastn[k] + lastslide*( ticks - 1 );
if ( n[k][0][0] > 856 ) n[k][0][0] = 856;
if ( n[k][0][0] > slideto ) n[k][0][0] = slideto;
}
else
{
n[k][0][0] = lastn[k] - lastslide * ( ticks - 1 );
if ( n[k][0][0] < 113 ) n[k][0][0] = 113;
if ( n[k][0][0] < slideto ) n[k][0][0] = slideto;
}
n[k][0][1] = vol[k];
break;
/* Slide up */
case 0x100:
/* Slide down */
case 0x200:
if ( ! ( effect & 0x0FF ) || sam->m > 127 ) break;
if ( effect & 0x200 ) lastslide = effect & 0x0FF;
else lastslide = -( effect & 0x0FF );
if ( !note )
if ( !delay[k] ) break;
else
{
n[k][0][0] = lastn[k] + lastslide;
n[k][0][1] = vol[k];
}
else n[k][0][0] += lastslide;
if ( n[k][0][0] > 856 ) n[k][0][0] = 856;
else
if ( n[k][0][0] < 113 ) n[k][0][0] = 113;
break;
/* Vibrato */
case 0x400:
/* Tremolo */
case 0x700:
// Ignore these effects.. not convertable
break;
/* Slide to & volume slide */
case 0x500:
if ( ( note || slideto ) && note != lastn[k] && sam->m < 128 )
{
if ( note ) slideto = note;
if ( slideto > lastn[k] )
{
n[k][0][0] = lastn[k] + lastslide * ( ticks - 1 );
if ( n[k][0][0] > 856 ) n[k][0][0] = 856;
if ( n[k][0][0] > slideto ) n[k][0][0] = slideto;
}
else
{
n[k][0][0] = lastn[k] - lastslide * ( ticks - 1 );
if ( n[k][0][0] < 113 ) n[k][0][0] = 113;
if ( n[k][0][0] < slideto ) n[k][0][0] = slideto;
}
}
else n[k][0][0] = 0;
note = 0;
/* Vibrato & volume slide */
case 0x600:
/* Volume slide */
case 0xA00:
{
int v;
if ( !note ) v = vol[k];
else v = sam->v;
v += ( ticks - 1 ) * ( effect & 0x0F0 ); // Can't really slide
v -= ( ticks - 1 ) * ( effect & 0x00F );
if ( v > 127 ) v = 127;
else if ( v < 0 ) v = 0;
n[k][0][1] = v;
break;
}
/* Set offset: pretend it's retrigger */
case 0x900:
if ( ( !n[k][0][0] || !sampnum ) && delay[k] )
{
n[k][0][0] = lastn[k];
n[k][0][1] = vol[k];
}
break;
/* Position jump: ignore, but break anyway */
case 0xB00:
patbreak = 1;
break;
/* Pattern break */
case 0xD00:
patbreak = 1 + 10 * ( effect & 0x0F0 ) + ( effect & 0x00F );
break;
/* Set volume */
case 0xC00:
n[k][0][1] = effect & 0x0FF;
break;
/* Set tempo */
case 0xF00:
{
int temp;
temp = effect & 0x0FF;
if ( !temp ) temp = 1;
if ( temp < 32 )
{
ticks = temp;
/* Tempos act strangely */
if ( TempoType )
{
bpm = 750 / temp;
x = 80000 * temp;
}
}
else
{
bpm = temp;
x = 60000000 / temp;
}
if ( i ) break; // Only write tempo on track 0
cnt += 6 + WriteVLQ( f2, timer );
timer = 0;
OutByte( f2, (unsigned char)255 ); // Meta-event
OutByte( f2, 81 ); // Set tempo
OutByte( f2, 3 );
OutByte( f2, x >> 16 );
OutByte( f2, ( x >> 8 ) & 0xFF );
OutByte( f2, x & 0xFF );
tempdone = 1;
break;
}
/* Extended effects */
case 0xE00:
switch ( effect & 0x0F0 )
{
/* Fine slide up */
case 0x010:
if ( !( effect & 0x00F ) || sam->m > 127 ) break;
if ( !note )
if ( !delay[k] ) break;
else
{
n[k][h][0] = lastn[k] + ( effect & 0x00F );
n[k][h][1] = vol[k];
}
else n[k][h][0] += effect & 0x00F;
break;
/* Fine slide down */
case 0x020:
if ( !( effect & 0x00F ) || sam->m > 127 ) break;
if ( !note )
if ( !delay[k] ) break;
else
{
n[k][h][0] = lastn[k] - ( effect & 0x00F );
n[k][h][1] = vol[k];
}
else n[k][h][0] -= effect & 0x00F;
break;
case 0x000: /* Set filter on/off */
case 0x030: /* glissando on/off */
case 0x040: /* set vibrato wave */
case 0x050: /* set finetune */
case 0x060: /* pattern loop */
case 0x070: /* set tremolo wave */
case 0x080: /* un-used */
case 0x0F0: /* invert loop */
// Can't do these in MIDI.. ignore
break;
/* Fine volume slide up */
case 0x0A0:
/* Fine volume slide down */
case 0x0B0:
{
int v;
v = sam->v;
if ( effect & 0x0A0 ) v += effect & 0x00F;
else v -= effect & 0x00F;
if ( v < 0 ) v = 0;
else
if ( v > 127 ) v = 127;
n[k][0][1] = v;
break;
}
/* Retrigger sample */
case 0x090:
{
int a, b, c;
if ( !note && !delay[k] || !( effect & 0x00F ) ) break;
a = effect & 0x00F;
if ( !( ticks / a ) ) break;
if ( !note )
{
n[k][0][0] = lastn[k];
n[k][0][1] = vol[k];
}
c = 0;
b = 1;
a *= 48;
while ( c < 48 )
{
n[k][c][0] = note;
n[k][c][1] = n[k][0][1];
c = b * a / ticks;
b++;
}
break;
}
/* Cut sample */
case 0x0C0:
{
int a;
if ( !note && !delay[k] ) break;
a = 48 * ( effect & 0x00F ) / ticks;
if ( a > 47 ) break;
if ( note ) n[k][a][0] = note;
else n[k][a][0] = lastn[k];
n[k][a][1] = 0;
break;
}
/* Delay sample */
case 0x0D0:
{
int a;
if ( !note || !( effect & 0x00F ) ) break;
a = 48 * ( effect & 0x00F ) / ticks;
n[k][0][0] = 0;
if ( a > 47 ) break;
n[k][a][0] = note;
n[k][a][1] = n[k][a][0];
break;
}
/* Pattern pause */
case 0x0E0:
pause = 48 * ( effect & 0x00F );
break;
}
break;
}
}
for ( h = 0; h < 48; h++ )
{
for ( k = 0; k < 4; k++ )
if ( n[k][h][0] )
{
/* Turn off old note on same channel */
if ( delay[k] )
{
cnt += 3 + WriteVLQ( f2, timer );
timer = 0;
OutByte( f2, 0x80 + c ); // Note off
OutByte( f2, ENOTE( lastn[k], 0 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[1] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x80 + c );
OutByte( f2, ENOTE( lastn[k], 1 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[2] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x80 + c );
OutByte( f2, ENOTE( lastn[k], 2 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
}
}
delay[k] = 0;
}
lastn[k] = n[k][h][0];
n[k][h][0] = 0;
vol[k] = n[k][h][1];
cnt += 3 + WriteVLQ( f2, timer );
timer = 0;
OutByte( f2, 0x90 + c ); // Note on
OutByte( f2, ENOTE( lastn[k], 0 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[1] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x90 + c );
OutByte( f2, ENOTE( lastn[k], 1 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[2] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x90 + c );
OutByte( f2, ENOTE( lastn[k], 2 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
}
}
delay[k] = NoteLength( ANOTE( lastn[k] ), sam->l, bpm );
}
else
if ( delay[k] == 1 )
{
delay[k] = 0;
cnt += 3 + WriteVLQ( f2, timer );
timer = 0;
OutByte( f2, 0x80 + c ); // Note off
OutByte( f2, ENOTE( lastn[k], 0 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[1] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x80 + c );
OutByte( f2, ENOTE( lastn[k], 1 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[2] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x80 + c );
OutByte( f2, ENOTE( lastn[k], 2 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
}
}
}
else
if ( delay[k] > 0 ) delay[k]--;
timer++;
}
timer += pause;
if ( patbreak < 0 ) patbreak++;
else
if ( patbreak > 0 )
{
patbreak = 1 - patbreak;
while ( j )
{
j--;
Inskipp( f1, 16 ); // 16 bytes / song position
}
}
}
}
for ( k = 0; k < 4; k++ )
if ( delay[k] )
{
cnt += 3 + WriteVLQ( f2, timer );
timer = 0;
OutByte( f2, 0x80 + c ); // Note off
OutByte( f2, ENOTE( lastn[k], 0 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[1] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x80 + c );
OutByte( f2, ENOTE( lastn[k], 1 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
if ( sam->t[2] )
{
cnt += 4;
OutByte( f2, 0 );
OutByte( f2, 0x80 + c );
OutByte( f2, ENOTE( lastn[k], 2 ) );
OutByte( f2, RestrictVol( EVOL( vol[k] ) ) );
}
}
}
}
if ( !i && !tempdone )
{
cnt += 7;
OutByte( f2, 0 ); // Give the default 128 bpm if none done yet
OutByte( f2, (unsigned char)255 );
OutByte( f2, 81 );
OutByte( f2, 3 );
OutByte( f2, 7 );
OutByte( f2, 39 );
OutByte( f2, 14 );
}
cnt += 3 + WriteVLQ( f2, timer );
OutByte( f2, (unsigned char)255 );
OutByte( f2, 47 );
OutByte( f2, 0 );
AddToLog( inst, cnt ); // Process this later
FGoto( f1, p2 );
}
FlushOut( f2 );
WriteLog( f2->f );
FGoto( f1, p1 );
}